package org.budo.support.spring.expression.util;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.budo.support.spring.expression.accessor.MapNotContainsKeyAccessor;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

/**
 * @author lmw
 */
public class SpelUtil {
    private static final ParserContext _PARSER_CONTEXT = new TemplateParserContext();

    private static final Map<String, Expression> _EXPRESSION_CACHE = new ConcurrentHashMap<String, Expression>(512);

    private static ExpressionParser _EXPRESSION_PARSER;

    private static ExpressionParser expressionParser() {
        if (null != _EXPRESSION_PARSER) {
            return _EXPRESSION_PARSER;
        }

        SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
        return _EXPRESSION_PARSER = new SpelExpressionParser(configuration);
    }

    public static Expression expression(String template) {
        if (null == template) {
            return null;
        }

        Expression expression = _EXPRESSION_CACHE.get(template);
        if (null != expression) {
            return expression;
        }

        expression = expressionParser().parseExpression(template, _PARSER_CONTEXT);
        _EXPRESSION_CACHE.put(template, expression); //
        return expression;
    }

    public static EvaluationContext evaluationContext(Map<String, Object> map) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.addPropertyAccessor(new MapAccessor());
        context.addPropertyAccessor(new MapNotContainsKeyAccessor());
        context.setRootObject(map);
        return context;
    }

    public static String merge(String template, Map<String, Object> map) {
        if (null == template || !template.startsWith("#{")) {
            throw new IllegalArgumentException("#62 template=" + template + ", map=" + map);
        }

        Expression expression = expression(template);
        if (null == expression) {
            return null;
        }

        EvaluationContext context = evaluationContext(map);

        return expression.getValue(context, String.class);
    }
}